package models

import (
	"bytes"
	"codeless/utils"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"strconv"
	"strings"
)

var validate = validator.New()

var formatFnMap = map[string]func(val interface{}) interface{}{}
var defaultFnMap = map[string]func() interface{}{}

func registerDefault(name string, handler func() interface{}) {
	name = strings.ToLower(name)
	defaultFnMap[name] = handler
}
func registerFormat(name string, handler func(val interface{}) interface{}) {
	name = strings.ToLower(name)
	formatFnMap[name] = handler
}
func init() {
	registerDefault("@time", timeIntDefault)
	registerDefault("@timeint", timeIntDefault)
	registerDefault("@dayint", dayIntDefault)
	registerDefault("@daystr", dayStrDefault)
	registerDefault("@id", idDefault)
	registerFormat("@md5", md5Format)
	registerFormat("@pass", hashFormat)
	registerFormat("@like", likeFormat)
	registerFormat("@llike", leftLikeFormat)
	registerFormat("@rlike", rightLikeFormat)
	registerFormat("@dayrint", dayRInt)
	registerFormat("@daypint", dayPInt)
}

type ModInfo struct {
	Name    string                   `json:"name"`
	Table   CrudTable                `json:"table"`
	Columns map[string]CrudModColumn `json:"columns"`
	Unique  []CrudUnique             `json:"unique"`
}

func (c *ModInfo) GetKeyCols() map[string]CrudModColumn {
	keys := make(map[string]CrudModColumn)
	for k, v := range c.Columns {
		if v.Key {
			keys[k] = v
		}
	}
	return keys
}

type CrudTable struct {
	Name   string `json:"name"`
	ZhName string `json:"zh_name"`
}
type CrudModColumn struct {
	ZhName   string      `json:"zh_name"`
	DbType   string      `json:"db_type"`
	LangType string      `json:"lang_type"`
	Default  interface{} `json:"default"`
	Format   string      `json:"format"`
	Key      bool        `json:"key"`
	Index    string      `json:"index"`
}

func parseDefaultVal(langType string, defVal interface{}) interface{} {

	//处理json string 里面是int 但unmarshal 后为float
	if defVal != nil {
		ditem, ok := defVal.(string)
		if ok {
			if dfn, dok := defaultFnMap[ditem]; dok {
				return dfn()
			}
		}
		if langType != "int" {
			return defVal
		}
		item, ok := defVal.(float64)
		if ok {
			return int64(item)
		} else {
			return 0
		}
	} else {
		if langType == "int" || langType == "float" {
			return 0
		} else {
			return ""
		}
	}

}

func (c *CrudModColumn) parseDefaultVal() interface{} {
	return parseDefaultVal(c.LangType, c.Default)
}
func (c *CrudModColumn) checkVal(val string) CheckColumnResp {

	if c.LangType == "int" {
		pval, per := strconv.ParseInt(val, 10, 64)
		msg := ""
		if per != nil {
			pval = 0
			msg = c.ZhName + "数据不合法"
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else if c.LangType == "float" {
		pval, per := strconv.ParseFloat(val, 64)
		msg := ""
		if per != nil {
			pval = 0
			msg = c.ZhName + "数据不合法"
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else {
		return CheckColumnResp{
			Valid:    true,
			Msg:      "",
			Default:  "",
			ParseVal: val,
		}
	}
}
func (c *CrudModColumn) checkValInterface(val interface{}) CheckColumnResp {

	if c.LangType == "int" {
		str := fmt.Sprintf("%v", val)
		pval, per := strconv.ParseInt(str, 10, 64)
		msg := ""
		if per != nil {
			pval = 0
			msg = c.ZhName + "数据不合法"
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else if c.LangType == "float" {
		str := fmt.Sprintf("%v", val)
		pval, per := strconv.ParseFloat(str, 64)
		msg := ""
		if per != nil {
			pval = 0
			msg = c.ZhName + "数据不合法"
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else {
		return CheckColumnResp{
			Valid:    true,
			Msg:      "",
			Default:  "",
			ParseVal: val,
		}
	}
}

type CrudColumn struct {
	Columns map[string]CrudField `json:"columns"`
}

type CrudField struct {
	ZhName string `json:"zh_name"`
	Rule   string `json:"rule"`
}

func (c *CrudColumn) GetRules() map[string]interface{} {
	rules := make(map[string]interface{})
	for k, v := range c.Columns {
		if v.Rule != "" {
			rules[k] = v.Rule
		}
	}
	return rules
}

type ValidResp struct {
	Valid bool
	Msg   string
}
type CheckColumnResp struct {
	Valid    bool
	Msg      string
	Default  interface{}
	ParseVal interface{}
}

func (c *CrudColumn) GetFormData(columMap map[string]CrudModColumn, ctx *gin.Context, create bool) (map[string]interface{}, map[string]interface{}, ValidResp) {
	formData := make(map[string]interface{})
	dbData := make(map[string]interface{})
	//生成form data
	for k, _ := range c.Columns {
		val := ctx.PostForm(k)
		dbCol, dbOk := columMap[k]
		if !dbOk {
			return nil, nil, ValidResp{
				Valid: false,
				Msg:   "数据不合法,存在未定义字段" + k,
			}
		}
		if len(val) > 0 {
			checkResp := dbCol.checkVal(val)
			if !checkResp.Valid {
				return nil, nil, ValidResp{
					Valid: false,
					Msg:   checkResp.Msg,
				}
			} else {
				formData[k] = checkResp.ParseVal
			}
		}

	}
	//检验form 上传是否合法
	rules := c.GetRules()
	if len(rules) != 0 {
		errs := validate.ValidateMap(formData, rules)
		if len(errs) > 0 {
			firstKey := ""
			for k, _ := range errs {
				firstKey = k
				break
			}
			return nil, nil, ValidResp{
				Valid: false,
				Msg:   "【" + firstKey + "】数据校验不通过",
			}
		}
	}
	if create {
		for k, dbCol := range columMap {
			formVal, formOk := formData[k]
			if formOk {
				dbData[k] = formVal
			} else {
				dbData[k] = dbCol.parseDefaultVal()
				//判断是key并且值为空，则默认创建id
				if dbCol.Key && dbCol.LangType == "string" && dbData[k] == "" {
					dbData[k] = utils.NewId()
				}
			}
			if dbCol.Format != "" {
				if formatFn, fok := formatFnMap[dbCol.Format]; fok {
					dbData[k] = formatFn(dbData[k])
				}
			}
		}
	} else {
		for k, dbCol := range columMap {
			//去掉主键
			if dbCol.Key {
				continue
			}
			_, upOk := c.Columns[k]
			if !upOk {
				continue
			}
			formVal, formOk := formData[k]
			if formOk {
				dbData[k] = formVal
			} else {
				dbData[k] = dbCol.parseDefaultVal()
			}
			if dbCol.Format != "" {
				if formatFn, fok := formatFnMap[dbCol.Format]; fok {
					dbData[k] = formatFn(dbData[k])
				}
			}
		}
	}
	return formData, dbData, ValidResp{
		Valid: true,
		Msg:   "",
	}
}
func (c *CrudColumn) GetFormDataFromMap(columMap map[string]CrudModColumn, argMap map[string]interface{}, create bool) (map[string]interface{}, map[string]interface{}, ValidResp) {
	formData := make(map[string]interface{})
	dbData := make(map[string]interface{})
	//生成form data
	for k, _ := range c.Columns {
		val := argMap[k]
		dbCol, dbOk := columMap[k]
		if !dbOk {
			return nil, nil, ValidResp{
				Valid: false,
				Msg:   "数据不合法,存在未定义字段" + k,
			}
		}
		if val != nil {
			checkResp := dbCol.checkValInterface(val)
			if !checkResp.Valid {
				return nil, nil, ValidResp{
					Valid: false,
					Msg:   checkResp.Msg,
				}
			} else {
				formData[k] = checkResp.ParseVal
			}
		}

	}
	//检验form 上传是否合法
	rules := c.GetRules()
	if len(rules) != 0 {
		errs := validate.ValidateMap(formData, rules)
		if len(errs) > 0 {
			firstKey := ""
			for k, _ := range errs {
				firstKey = k
				break
			}
			return nil, nil, ValidResp{
				Valid: false,
				Msg:   "【" + firstKey + "】数据校验不通过",
			}
		}
	}
	if create {
		for k, dbCol := range columMap {
			formVal, formOk := formData[k]
			if formOk {
				dbData[k] = formVal
			} else {
				dbData[k] = dbCol.parseDefaultVal()
				//判断是key并且值为空，则默认创建id
				if dbCol.Key && dbCol.LangType == "string" && dbData[k] == "" {
					dbData[k] = utils.NewId()
				}
			}
			if dbCol.Format != "" {
				if formatFn, fok := formatFnMap[dbCol.Format]; fok {
					dbData[k] = formatFn(dbData[k])
				}
			}
		}
	} else {
		for k, dbCol := range columMap {
			//去掉主键
			if dbCol.Key {
				continue
			}
			_, upOk := c.Columns[k]
			if !upOk {
				continue
			}
			formVal, formOk := formData[k]
			if formOk {
				dbData[k] = formVal
			} else {
				dbData[k] = dbCol.parseDefaultVal()
			}
			if dbCol.Format != "" {
				if formatFn, fok := formatFnMap[dbCol.Format]; fok {
					dbData[k] = formatFn(dbData[k])
				}
			}
		}
	}
	return formData, dbData, ValidResp{
		Valid: true,
		Msg:   "",
	}
}

type CrudAdd struct {
	Opt
	Sql    string `json:"sql"`
	Into   string `json:"into"`
	Before string `json:"before"`
	After  string `json:"after"`
	CrudColumn
}

func (c *CrudAdd) checkDisable() {
	if len(c.Columns) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}
func (c *CrudGet) checkDisable() {
	if len(c.Select) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}

type CrudList struct {
	Plain bool `json:"plain"`
	CrudQuery
}
type CrudPage struct {
	Count string `json:"count"`
	CrudQuery
}
type CrudGet struct {
	Plain bool `json:"plain"`
	CrudQuery
}

func (c *CrudGet) IsValid(ctx *gin.Context) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParse(ctx)
	if !valid {
		return "数据不合法"
	}
	return ""
}
func (c *CrudGet) IsValidMap(argMap map[string]interface{}) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParseMap(argMap)
	if !valid {
		return "数据不合法"
	}
	return ""
}
func (c *CrudPage) checkDisable() {
	if len(c.Select) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}
func (c *CrudPage) IsValid(ctx *gin.Context) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParse(ctx)
	if !valid {
		return "数据不合法"
	}
	return ""
}
func (c *CrudPage) IsValidMap(argMap map[string]interface{}) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParseMap(argMap)
	if !valid {
		return "数据不合法"
	}
	return ""
}
func (c *CrudList) checkDisable() {
	if len(c.Select) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}

func (c *CrudList) IsValid(ctx *gin.Context) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParse(ctx)
	if !valid {
		return "数据不合法"
	}
	return ""
}
func (c *CrudList) IsValidMap(argMap map[string]interface{}) string {
	if c.Disable {
		return "未启用查询,请检查配置文件"
	}
	valid := c.UnStrictParseMap(argMap)
	if !valid {
		return "数据不合法"
	}
	return ""
}

type CrudDel struct {
	Fake   bool   `json:"fake"`
	Before string `json:"before"`
	After  string `json:"after"`
	CrudQuery
}

type CrudQuery struct {
	Opt
	Select string      `json:"select"`
	From   string      `json:"from"`
	Sql    string      `json:"sql"`
	Where  []CrudWhere `json:"where"`
	Args   []CrudArgs  `json:"args"`
}
type CrudWhere struct {
	Name string            `json:"name"`
	On   map[string]string `json:"on"`
}

type CrudArgs struct {
	Name     string      `json:"name"`
	LangType string      `json:"lang_type"`
	Title    string      `json:"title"`
	Ignore   bool        `json:"ignore"`
	Format   string      `json:"format"`
	Val      interface{} `json:"-"`
	Default  interface{} `json:"default"`
}

func (c *CrudArgs) checkVal(val string) CheckColumnResp {

	if c.LangType == "int" {
		pval, per := strconv.ParseInt(val, 10, 64)
		msg := ""
		if per != nil {
			pval = 0
			if c.Title != "" {
				msg = c.Title + "数据不合法"
			} else {
				msg = c.Name + "数据不合法"
			}

		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else if c.LangType == "float" {
		pval, per := strconv.ParseFloat(val, 64)
		msg := ""
		if per != nil {
			pval = 0
			if c.Title != "" {
				msg = c.Title + "数据不合法"
			} else {
				msg = c.Name + "数据不合法"
			}
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else {
		return CheckColumnResp{
			Valid:    true,
			Msg:      "",
			Default:  "",
			ParseVal: val,
		}
	}
}
func (c *CrudArgs) checkValInterface(val interface{}) CheckColumnResp {

	if c.LangType == "int" {
		str := fmt.Sprintf("%v", val)
		pval, per := strconv.ParseInt(str, 10, 64)
		msg := ""
		if per != nil {
			pval = 0
			if c.Title != "" {
				msg = c.Title + "数据不合法"
			} else {
				msg = c.Name + "数据不合法"
			}

		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else if c.LangType == "float" {
		str := fmt.Sprintf("%v", val)
		pval, per := strconv.ParseFloat(str, 64)
		msg := ""
		if per != nil {
			pval = 0
			if c.Title != "" {
				msg = c.Title + "数据不合法"
			} else {
				msg = c.Name + "数据不合法"
			}
		}
		return CheckColumnResp{
			Valid:    per == nil,
			Msg:      msg,
			Default:  0,
			ParseVal: pval,
		}
	} else {
		return CheckColumnResp{
			Valid:    true,
			Msg:      "",
			Default:  "",
			ParseVal: val,
		}
	}
}
func (c *CrudArgs) parseDefaultVal() interface{} {

	if c.Default != nil {
		if c.LangType != "int" {
			return c.Default
		}
		item, ok := c.Default.(float64)
		if ok {
			return int64(item)
		} else {
			return 0
		}
	} else {
		if c.LangType == "int" || c.LangType == "float" {
			return 0
		} else {
			return ""
		}
	}

}
func (c *CrudQuery) WhereSqlMap(preSql string) (map[string]string, error) {
	sqlMap := make(map[string]string)
	for _, v := range c.Where {
		whereBuf := &bytes.Buffer{}
		argMap := c.GetArgValMap()
		for exp, sv := range v.On {
			flag, expErr := utils.ExpOn(exp, argMap)
			if expErr != nil {
				return sqlMap, expErr
			}
			if !flag {
				continue
			}
			whereBuildStr(whereBuf, sv)
		}
		//处理掉只有where
		preSql = strings.ReplaceAll(preSql, "${where}", "")
		whereSql := strings.TrimPrefix(whereBuf.String(), " AND")
		if strings.Contains(preSql, "where") {

			sqlMap[v.Name] = whereSql
		} else {
			if whereSql != "" {
				sqlMap[v.Name] = " WHERE " + whereSql
			} else {
				sqlMap[v.Name] = ""
			}
		}
	}
	return sqlMap, nil
}
func (c *CrudQuery) GetArgValMap() map[string]interface{} {
	valMap := make(map[string]interface{})
	for _, v := range c.Args {
		if v.Val != nil {
			if v.Format != "" {
				if formatFn, fok := formatFnMap[v.Format]; fok {
					valMap[v.Name] = formatFn(v.Val)
				}
			} else {
				valMap[v.Name] = v.Val
			}
		} else {
			//增加了nil 判断
			valMap[v.Name] = ""
		}
	}
	return valMap
}
func whereBuildStr(sb *bytes.Buffer, v string) {
	sb.WriteString(" AND ")
	sb.WriteString(v)
}
func (c *CrudQuery) UnStrictParse(ctx *gin.Context) bool {
	for idx, _ := range c.Args {
		item := c.Args[idx]
		if item.Ignore {
			c.Args[idx].Val = item.parseDefaultVal()
		} else {
			val := ctx.PostForm(item.Name)
			if val != "" {
				checkResp := item.checkVal(val)
				if checkResp.Valid {
					c.Args[idx].Val = checkResp.ParseVal
				} else {
					c.Args[idx].Val = checkResp.Default
				}
			} else {
				c.Args[idx].Val = item.parseDefaultVal()
			}
		}
	}
	return true
}
func (c *CrudQuery) UnStrictParseMap(argMap map[string]interface{}) bool {
	for idx, _ := range c.Args {
		item := c.Args[idx]
		if item.Ignore {
			c.Args[idx].Val = item.parseDefaultVal()
		} else {
			val := argMap[item.Name]
			if val != nil {
				checkResp := item.checkValInterface(val)
				if checkResp.Valid {
					c.Args[idx].Val = checkResp.ParseVal
				} else {
					c.Args[idx].Val = checkResp.Default
				}
			} else {
				c.Args[idx].Val = item.parseDefaultVal()
			}
		}
	}
	return true
}
func (c *CrudQuery) StrictParse(ctx *gin.Context) bool {
	if len(c.Where) == 0 && c.Sql == "" {
		return false
	}
	for idx, _ := range c.Args {
		item := c.Args[idx]
		if item.Ignore {
			c.Args[idx].Val = item.parseDefaultVal()
		} else {
			val := ctx.PostForm(item.Name)
			if val != "" {
				checkResp := item.checkVal(val)
				if !checkResp.Valid {
					return false
				} else {
					c.Args[idx].Val = checkResp.ParseVal
				}
			} else {
				if item.Default != nil {
					c.Args[idx].Val = item.parseDefaultVal()
				} else {
					return false
				}
			}
		}

	}
	return true
}
func (c *CrudQuery) StrictParseFromMap(argMap map[string]interface{}) bool {
	if len(c.Where) == 0 {
		return false
	}
	for idx, _ := range c.Args {
		item := c.Args[idx]
		if item.Ignore {
			c.Args[idx].Val = item.parseDefaultVal()
		} else {
			val := argMap[item.Name]
			if val != nil {
				c.Args[idx].Val = val
			} else {
				if item.Default != nil {
					c.Args[idx].Val = item.parseDefaultVal()
				} else {
					return false
				}
			}
		}

	}
	return true
}

type CrudUpdate struct {
	Before string `json:"before"`
	After  string `json:"after"`
	CrudColumn
	CrudQuery
}

func (c *CrudUpdate) checkDisable() {
	if len(c.Where) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}

type Opt struct {
	Disable bool `json:"disable"`
}

func (c *CrudDel) checkDisable() {
	if len(c.Where) <= 0 && c.Sql == "" {
		c.Opt.Disable = true
	}
}

type CrudUnique struct {
	Columns []string `json:"columns"`
	Tip     string   `json:"tip"`
}
